home *** CD-ROM | disk | FTP | other *** search
/ PC/CD Gamer UK 120 / CD Gamer Issue 120 (March 2003) (Disc 2).ISO / mods / Q2_Codered / codeRED1_0.exe / Data1.cab / sv_main.c < prev    next >
Encoding:
C/C++ Source or Header  |  2002-12-21  |  25.0 KB  |  1,061 lines

  1. /*
  2. Copyright (C) 1997-2001 Id Software, Inc.
  3.  
  4. This program is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU General Public License
  6. as published by the Free Software Foundation; either version 2
  7. of the License, or (at your option) any later version.
  8.  
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
  12.  
  13. See the GNU General Public License for more details.
  14.  
  15. You should have received a copy of the GNU General Public License
  16. along with this program; if not, write to the Free Software
  17. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  18.  
  19. */
  20.  
  21. #include "server.h"
  22.  
  23. netadr_t    master_adr[MAX_MASTERS];    // address of group servers
  24.  
  25. client_t    *sv_client;            // current client
  26.  
  27. cvar_t    *sv_paused;
  28. cvar_t    *sv_timedemo;
  29.  
  30. cvar_t    *sv_enforcetime;
  31.  
  32. cvar_t    *timeout;                // seconds without any message
  33. cvar_t    *zombietime;            // seconds to sink messages after disconnect
  34.  
  35. cvar_t    *rcon_password;            // password for remote server commands
  36.  
  37. cvar_t    *allow_download;
  38. cvar_t *allow_download_players;
  39. cvar_t *allow_download_models;
  40. cvar_t *allow_download_sounds;
  41. cvar_t *allow_download_maps;
  42.  
  43. cvar_t *sv_airaccelerate;
  44.  
  45. cvar_t    *sv_noreload;            // don't reload level state when reentering
  46.  
  47. cvar_t    *maxclients;            // FIXME: rename sv_maxclients
  48. cvar_t    *sv_showclamp;
  49.  
  50. cvar_t    *hostname;
  51. cvar_t    *public_server;            // should heartbeats be sent
  52.  
  53. cvar_t    *sv_reconnect_limit;    // minimum seconds between connect messages
  54.  
  55. void Master_Shutdown (void);
  56.  
  57.  
  58. //============================================================================
  59.  
  60.  
  61. /*
  62. =====================
  63. SV_DropClient
  64.  
  65. Called when the player is totally leaving the server, either willingly
  66. or unwillingly.  This is NOT called if the entire server is quiting
  67. or crashing.
  68. =====================
  69. */
  70. void SV_DropClient (client_t *drop)
  71. {
  72.     // add the disconnect
  73.     MSG_WriteByte (&drop->netchan.message, svc_disconnect);
  74.  
  75.     if (drop->state == cs_spawned)
  76.     {
  77.         // call the prog function for removing a client
  78.         // this will remove the body, among other things
  79.         ge->ClientDisconnect (drop->edict);
  80.     }
  81.  
  82.     if (drop->download)
  83.     {
  84.         FS_FreeFile (drop->download);
  85.         drop->download = NULL;
  86.     }
  87.  
  88.     drop->state = cs_zombie;        // become free in a few seconds
  89.     drop->name[0] = 0;
  90. }
  91.  
  92.  
  93.  
  94. /*
  95. ==============================================================================
  96.  
  97. CONNECTIONLESS COMMANDS
  98.  
  99. ==============================================================================
  100. */
  101.  
  102. /*
  103. ===============
  104. SV_StatusString
  105.  
  106. Builds the string that is sent as heartbeats and status replies
  107. ===============
  108. */
  109. char    *SV_StatusString (void)
  110. {
  111.     char    player[1024];
  112.     static char    status[MAX_MSGLEN - 16];
  113.     int        i;
  114.     client_t    *cl;
  115.     int        statusLength;
  116.     int        playerLength;
  117.  
  118.     strcpy (status, Cvar_Serverinfo());
  119.     strcat (status, "\n");
  120.     statusLength = strlen(status);
  121.  
  122.     for (i=0 ; i<maxclients->value ; i++)
  123.     {
  124.         cl = &svs.clients[i];
  125.         if (cl->state == cs_connected || cl->state == cs_spawned )
  126.         {
  127.             Com_sprintf (player, sizeof(player), "%i %i \"%s\"\n", 
  128.                 cl->edict->client->ps.stats[STAT_FRAGS], cl->ping, cl->name);
  129.             playerLength = strlen(player);
  130.             if (statusLength + playerLength >= sizeof(status) )
  131.                 break;        // can't hold any more
  132.             strcpy (status + statusLength, player);
  133.             statusLength += playerLength;
  134.         }
  135.     }
  136.  
  137.     return status;
  138. }
  139.  
  140. /*
  141. ================
  142. SVC_Status
  143.  
  144. Responds with all the info that qplug or qspy can see
  145. ================
  146. */
  147. void SVC_Status (void)
  148. {
  149.     Netchan_OutOfBandPrint (NS_SERVER, net_from, "print\n%s", SV_StatusString());
  150. #if 0
  151.     Com_BeginRedirect (RD_PACKET, sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_FlushRedirect);
  152.     Com_Printf (SV_StatusString());
  153.     Com_EndRedirect ();
  154. #endif
  155. }
  156.  
  157. /*
  158. ================
  159. SVC_Ack
  160.  
  161. ================
  162. */
  163. void SVC_Ack (void)
  164. {
  165.     Com_Printf ("Ping acknowledge from %s\n", NET_AdrToString(net_from));
  166. }
  167.  
  168. /*
  169. ================
  170. SVC_Info
  171.  
  172. Responds with short info for broadcast scans
  173. The second parameter should be the current protocol version number.
  174. ================
  175. */
  176. void SVC_Info (void)
  177. {
  178.     char    string[64];
  179.     int        i, count;
  180.     int        version;
  181.  
  182.     if (maxclients->value == 1)
  183.         return;        // ignore in single player
  184.  
  185.     version = atoi (Cmd_Argv(1));
  186.  
  187.     if (version != PROTOCOL_VERSION)
  188.         Com_sprintf (string, sizeof(string), "%s: wrong version\n", hostname->string, sizeof(string));
  189.     else
  190.     {
  191.         count = 0;
  192.         for (i=0 ; i<maxclients->value ; i++)
  193.             if (svs.clients[i].state >= cs_connected)
  194.                 count++;
  195.  
  196.         Com_sprintf (string, sizeof(string), "%16s %8s %2i/%2i\n", hostname->string, sv.name, count, (int)maxclients->value);
  197.     }
  198.  
  199.     Netchan_OutOfBandPrint (NS_SERVER, net_from, "info\n%s", string);
  200. }
  201.  
  202. /*
  203. ================
  204. SVC_Ping
  205.  
  206. Just responds with an acknowledgement
  207. ================
  208. */
  209. void SVC_Ping (void)
  210. {
  211.     Netchan_OutOfBandPrint (NS_SERVER, net_from, "ack");
  212. }
  213.  
  214.  
  215. /*
  216. =================
  217. SVC_GetChallenge
  218.  
  219. Returns a challenge number that can be used
  220. in a subsequent client_connect command.
  221. We do this to prevent denial of service attacks that
  222. flood the server with invalid connection IPs.  With a
  223. challenge, they must give a valid IP address.
  224. =================
  225. */
  226. void SVC_GetChallenge (void)
  227. {
  228.     int        i;
  229.     int        oldest;
  230.     int        oldestTime;
  231.  
  232.     oldest = 0;
  233.     oldestTime = 0x7fffffff;
  234.  
  235.     // see if we already have a challenge for this ip
  236.     for (i = 0 ; i < MAX_CHALLENGES ; i++)
  237.     {
  238.         if (NET_CompareBaseAdr (net_from, svs.challenges[i].adr))
  239.             break;
  240.         if (svs.challenges[i].time < oldestTime)
  241.         {
  242.             oldestTime = svs.challenges[i].time;
  243.             oldest = i;
  244.         }
  245.     }
  246.  
  247.     if (i == MAX_CHALLENGES)
  248.     {
  249.         // overwrite the oldest
  250.         svs.challenges[oldest].challenge = rand() & 0x7fff;
  251.         svs.challenges[oldest].adr = net_from;
  252.         svs.challenges[oldest].time = curtime;
  253.         i = oldest;
  254.     }
  255.  
  256.     // send it back
  257.     Netchan_OutOfBandPrint (NS_SERVER, net_from, "challenge %i", svs.challenges[i].challenge);
  258. }
  259.  
  260. /*
  261. ==================
  262. SVC_DirectConnect
  263.  
  264. A connection request that did not come from the master
  265. ==================
  266. */
  267. void SVC_DirectConnect (void)
  268. {
  269.     char        userinfo[MAX_INFO_STRING];
  270.     netadr_t    adr;
  271.     int            i;
  272.     client_t    *cl, *newcl;
  273.     client_t    temp;
  274.     edict_t        *ent;
  275.     int            edictnum;
  276.     int            version;
  277.     int            qport;
  278.     int            challenge;
  279.  
  280.     adr = net_from;
  281.  
  282.     Com_DPrintf ("SVC_DirectConnect ()\n");
  283.  
  284.     version = atoi(Cmd_Argv(1));
  285.     if (version != PROTOCOL_VERSION)
  286.     {
  287.         Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nServer is version %4.2f.\n", VERSION);
  288.         Com_DPrintf ("    rejected connect from version %i\n", version);
  289.         return;
  290.     }
  291.  
  292.     qport = atoi(Cmd_Argv(2));
  293.  
  294.     challenge = atoi(Cmd_Argv(3));
  295.  
  296.     strncpy (userinfo, Cmd_Argv(4), sizeof(userinfo)-1);
  297.     userinfo[sizeof(userinfo) - 1] = 0;
  298.  
  299.     // force the IP key/value pair so the game can filter based on ip
  300.     Info_SetValueForKey (userinfo, "ip", NET_AdrToString(net_from));
  301.  
  302.     // attractloop servers are ONLY for local clients
  303.     if (sv.attractloop)
  304.     {
  305.         if (!NET_IsLocalAddress (adr))
  306.         {
  307.             Com_Printf ("Remote connect in attract loop.  Ignored.\n");
  308.             Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nConnection refused.\n");
  309.             return;
  310.         }
  311.     }
  312.  
  313.     // see if the challenge is valid
  314.     if (!NET_IsLocalAddress (adr))
  315.     {
  316.         for (i=0 ; i<MAX_CHALLENGES ; i++)
  317.         {
  318.             if (NET_CompareBaseAdr (net_from, svs.challenges[i].adr))
  319.             {
  320.                 if (challenge == svs.challenges[i].challenge)
  321.                     break;        // good
  322.                 Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nBad challenge.\n");
  323.                 return;
  324.             }
  325.         }
  326.         if (i == MAX_CHALLENGES)
  327.         {
  328.             Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nNo challenge for address.\n");
  329.             return;
  330.         }
  331.     }
  332.  
  333.     newcl = &temp;
  334.     memset (newcl, 0, sizeof(client_t));
  335.  
  336.     // if there is already a slot for this ip, reuse it
  337.     for (i=0,cl=svs.clients ; i<maxclients->value ; i++,cl++)
  338.     {
  339.         if (cl->state == cs_free)
  340.             continue;
  341.         if (NET_CompareBaseAdr (adr, cl->netchan.remote_address)
  342.             && ( cl->netchan.qport == qport 
  343.             || adr.port == cl->netchan.remote_address.port ) )
  344.         {
  345.             if (!NET_IsLocalAddress (adr) && (svs.realtime - cl->lastconnect) < ((int)sv_reconnect_limit->value * 1000))
  346.             {
  347.                 Com_DPrintf ("%s:reconnect rejected : too soon\n", NET_AdrToString (adr));
  348.                 return;
  349.             }
  350.             Com_Printf ("%s:reconnect\n", NET_AdrToString (adr));
  351.             newcl = cl;
  352.             goto gotnewcl;
  353.         }
  354.     }
  355.  
  356.     // find a client slot
  357.     newcl = NULL;
  358.     for (i=0,cl=svs.clients ; i<maxclients->value ; i++,cl++)
  359.     {
  360.         if (cl->state == cs_free)
  361.         {
  362.             newcl = cl;
  363.             break;
  364.         }
  365.     }
  366.     if (!newcl)
  367.     {
  368.         Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nServer is full.\n");
  369.         Com_DPrintf ("Rejected a connection.\n");
  370.         return;
  371.     }
  372.  
  373. gotnewcl:    
  374.     // build a new connection
  375.     // accept the new client
  376.     // this is the only place a client_t is ever initialized
  377.     *newcl = temp;
  378.     sv_client = newcl;
  379.     edictnum = (newcl-svs.clients)+1;
  380.     ent = EDICT_NUM(edictnum);
  381.     newcl->edict = ent;
  382.     newcl->challenge = challenge; // save challenge for checksumming
  383.  
  384.     // get the game a chance to reject this connection or modify the userinfo
  385.     if (!(ge->ClientConnect (ent, userinfo)))
  386.     {
  387.         if (*Info_ValueForKey (userinfo, "rejmsg")) 
  388.             Netchan_OutOfBandPrint (NS_SERVER, adr, "print\n%s\nConnection refused.\n",  
  389.                 Info_ValueForKey (userinfo, "rejmsg"));
  390.         else
  391.             Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nConnection refused.\n" );
  392.         Com_DPrintf ("Game rejected a connection.\n");
  393.         return;
  394.     }
  395.  
  396.     // parse some info from the info strings
  397.     strncpy (newcl->userinfo, userinfo, sizeof(newcl->userinfo)-1);
  398.     SV_UserinfoChanged (newcl);
  399.  
  400.     // send the connect packet to the client
  401.     Netchan_OutOfBandPrint (NS_SERVER, adr, "client_connect");
  402.  
  403.     Netchan_Setup (NS_SERVER, &newcl->netchan , adr, qport);
  404.  
  405.     newcl->state = cs_connected;
  406.     
  407.     SZ_Init (&newcl->datagram, newcl->datagram_buf, sizeof(newcl->datagram_buf) );
  408.     newcl->datagram.allowoverflow = true;
  409.     newcl->lastmessage = svs.realtime;    // don't timeout
  410.     newcl->lastconnect = svs.realtime;
  411. }
  412.  
  413. int Rcon_Validate (void)
  414. {
  415.     if (!strlen (rcon_password->string))
  416.         return 0;
  417.  
  418.     if (strcmp (Cmd_Argv(1), rcon_password->string) )
  419.         return 0;
  420.  
  421.     return 1;
  422. }
  423.  
  424. /*
  425. ===============
  426. SVC_RemoteCommand
  427.  
  428. A client issued an rcon command.
  429. Shift down the remaining args
  430. Redirect all printfs
  431. ===============
  432. */
  433. void SVC_RemoteCommand (void)
  434. {
  435.     int        i;
  436.     char    remaining[1024];
  437.  
  438.     i = Rcon_Validate ();
  439.  
  440.     if (i == 0)
  441.         Com_Printf ("Bad rcon from %s:\n%s\n", NET_AdrToString (net_from), net_message.data+4);
  442.     else
  443.         Com_Printf ("Rcon from %s:\n%s\n", NET_AdrToString (net_from), net_message.data+4);
  444.  
  445.     Com_BeginRedirect (RD_PACKET, sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_FlushRedirect);
  446.  
  447.     if (!Rcon_Validate ())
  448.     {
  449.         Com_Printf ("Bad rcon_password.\n");
  450.     }
  451.     else
  452.     {
  453.         remaining[0] = 0;
  454.  
  455.         for (i=2 ; i<Cmd_Argc() ; i++)
  456.         {
  457.             strcat (remaining, Cmd_Argv(i) );
  458.             strcat (remaining, " ");
  459.         }
  460.  
  461.         Cmd_ExecuteString (remaining);
  462.     }
  463.  
  464.     Com_EndRedirect ();
  465. }
  466.  
  467. /*
  468. =================
  469. SV_ConnectionlessPacket
  470.  
  471. A connectionless packet has four leading 0xff
  472. characters to distinguish it from a game channel.
  473. Clients that are in the game can still send
  474. connectionless packets.
  475. =================
  476. */
  477. void SV_ConnectionlessPacket (void)
  478. {
  479.     char    *s;
  480.     char    *c;
  481.  
  482.     MSG_BeginReading (&net_message);
  483.     MSG_ReadLong (&net_message);        // skip the -1 marker
  484.  
  485.     s = MSG_ReadStringLine (&net_message);
  486.  
  487.     Cmd_TokenizeString (s, false);
  488.  
  489.     c = Cmd_Argv(0);
  490.     Com_DPrintf ("Packet %s : %s\n", NET_AdrToString(net_from), c);
  491.  
  492.     if (!strcmp(c, "ping"))
  493.         SVC_Ping ();
  494.     else if (!strcmp(c, "ack"))
  495.         SVC_Ack ();
  496.     else if (!strcmp(c,"status"))
  497.         SVC_Status ();
  498.     else if (!strcmp(c,"info"))
  499.         SVC_Info ();
  500.     else if (!strcmp(c,"getchallenge"))
  501.         SVC_GetChallenge ();
  502.     else if (!strcmp(c,"connect"))
  503.         SVC_DirectConnect ();
  504.     else if (!strcmp(c, "rcon"))
  505.         SVC_RemoteCommand ();
  506.     else
  507.         Com_Printf ("bad connectionless packet from %s:\n%s\n"
  508.         , NET_AdrToString (net_from), s);
  509. }
  510.  
  511.  
  512. //============================================================================
  513.  
  514. /*
  515. ===================
  516. SV_CalcPings
  517.  
  518. Updates the cl->ping variables
  519. ===================
  520. */
  521. void SV_CalcPings (void)
  522. {
  523.     int            i, j;
  524.     client_t    *cl;
  525.     int            total, count;
  526.  
  527.     for (i=0 ; i<maxclients->value ; i++)
  528.     {
  529.         cl = &svs.clients[i];
  530.         if (cl->state != cs_spawned )
  531.             continue;
  532.  
  533. #if 0
  534.         if (cl->lastframe > 0)
  535.             cl->frame_latency[sv.framenum&(LATENCY_COUNTS-1)] = sv.framenum - cl->lastframe + 1;
  536.         else
  537.             cl->frame_latency[sv.framenum&(LATENCY_COUNTS-1)] = 0;
  538. #endif
  539.  
  540.         total = 0;
  541.         count = 0;
  542.         for (j=0 ; j<LATENCY_COUNTS ; j++)
  543.         {
  544.             if (cl->frame_latency[j] > 0)
  545.             {
  546.                 count++;
  547.                 total += cl->frame_latency[j];
  548.             }
  549.         }
  550.         if (!count)
  551.             cl->ping = 0;
  552.         else
  553. #if 0
  554.             cl->ping = total*100/count - 100;
  555. #else
  556.             cl->ping = total / count;
  557. #endif
  558.  
  559.         // let the game dll know about the ping
  560.         cl->edict->client->ping = cl->ping;
  561.     }
  562. }
  563.  
  564.  
  565. /*
  566. ===================
  567. SV_GiveMsec
  568.  
  569. Every few frames, gives all clients an allotment of milliseconds
  570. for their command moves.  If they exceed it, assume cheating.
  571. ===================
  572. */
  573. void SV_GiveMsec (void)
  574. {
  575.     int            i;
  576.     client_t    *cl;
  577.  
  578.     if (sv.framenum & 15)
  579.         return;
  580.  
  581.     for (i=0 ; i<maxclients->value ; i++)
  582.     {
  583.         cl = &svs.clients[i];
  584.         if (cl->state == cs_free )
  585.             continue;
  586.         
  587.         cl->commandMsec = 1800;        // 1600 + some slop
  588.     }
  589. }
  590.  
  591.  
  592. /*
  593. =================
  594. SV_ReadPackets
  595. =================
  596. */
  597. void SV_ReadPackets (void)
  598. {
  599.     int            i;
  600.     client_t    *cl;
  601.     int            qport;
  602.  
  603.     while (NET_GetPacket (NS_SERVER, &net_from, &net_message))
  604.     {
  605.         // check for connectionless packet (0xffffffff) first
  606.         if (*(int *)net_message.data == -1)
  607.         {
  608.             SV_ConnectionlessPacket ();
  609.             continue;
  610.         }
  611.  
  612.         // read the qport out of the message so we can fix up
  613.         // stupid address translating routers
  614.         MSG_BeginReading (&net_message);
  615.         MSG_ReadLong (&net_message);        // sequence number
  616.         MSG_ReadLong (&net_message);        // sequence number
  617.         qport = MSG_ReadShort (&net_message) & 0xffff;
  618.  
  619.         // check for packets from connected clients
  620.         for (i=0, cl=svs.clients ; i<maxclients->value ; i++,cl++)
  621.         {
  622.             if (cl->state == cs_free)
  623.                 continue;
  624.             if (!NET_CompareBaseAdr (net_from, cl->netchan.remote_address))
  625.                 continue;
  626.             if (cl->netchan.qport != qport)
  627.                 continue;
  628.             if (cl->netchan.remote_address.port != net_from.port)
  629.             {
  630.                 Com_Printf ("SV_ReadPackets: fixing up a translated port\n");
  631.                 cl->netchan.remote_address.port = net_from.port;
  632.             }
  633.  
  634.             if (Netchan_Process(&cl->netchan, &net_message))
  635.             {    // this is a valid, sequenced packet, so process it
  636.                 if (cl->state != cs_zombie)
  637.                 {
  638.                     cl->lastmessage = svs.realtime;    // don't timeout
  639.                     SV_ExecuteClientMessage (cl);
  640.                 }
  641.             }
  642.             break;
  643.         }
  644.         
  645.         if (i != maxclients->value)
  646.             continue;
  647.     }
  648. }
  649.  
  650. /*
  651. ==================
  652. SV_CheckTimeouts
  653.  
  654. If a packet has not been received from a client for timeout->value
  655. seconds, drop the conneciton.  Server frames are used instead of
  656. realtime to avoid dropping the local client while debugging.
  657.  
  658. When a client is normally dropped, the client_t goes into a zombie state
  659. for a few seconds to make sure any final reliable message gets resent
  660. if necessary
  661. ==================
  662. */
  663. void SV_CheckTimeouts (void)
  664. {
  665.     int        i;
  666.     client_t    *cl;
  667.     int            droppoint;
  668.     int            zombiepoint;
  669.  
  670.     droppoint = svs.realtime - 1000*timeout->value;
  671.     zombiepoint = svs.realtime - 1000*zombietime->value;
  672.  
  673.     for (i=0,cl=svs.clients ; i<maxclients->value ; i++,cl++)
  674.     {
  675.         // message times may be wrong across a changelevel
  676.         if (cl->lastmessage > svs.realtime)
  677.             cl->lastmessage = svs.realtime;
  678.  
  679.         if (cl->state == cs_zombie
  680.         && cl->lastmessage < zombiepoint)
  681.         {
  682.             cl->state = cs_free;    // can now be reused
  683.             continue;
  684.         }
  685.         if ( (cl->state == cs_connected || cl->state == cs_spawned) 
  686.             && cl->lastmessage < droppoint)
  687.         {
  688.             SV_BroadcastPrintf (PRINT_HIGH, "%s timed out\n", cl->name);
  689.             SV_DropClient (cl); 
  690.             cl->state = cs_free;    // don't bother with zombie state
  691.         }
  692.     }
  693. }
  694.  
  695. /*
  696. ================
  697. SV_PrepWorldFrame
  698.  
  699. This has to be done before the world logic, because
  700. player processing happens outside RunWorldFrame
  701. ================
  702. */
  703. void SV_PrepWorldFrame (void)
  704. {
  705.     edict_t    *ent;
  706.     int        i;
  707.  
  708.     for (i=0 ; i<ge->num_edicts ; i++, ent++)
  709.     {
  710.         ent = EDICT_NUM(i);
  711.         // events only last for a single message
  712.         ent->s.event = 0;
  713.     }
  714.  
  715. }
  716.  
  717.  
  718. /*
  719. =================
  720. SV_RunGameFrame
  721. =================
  722. */
  723. void SV_RunGameFrame (void)
  724. {
  725.     if (host_speeds->value)
  726.         time_before_game = Sys_Milliseconds ();
  727.  
  728.     // we always need to bump framenum, even if we
  729.     // don't run the world, otherwise the delta
  730.     // compression can get confused when a client
  731.     // has the "current" frame
  732.     sv.framenum++;
  733.     sv.time = sv.framenum*100;
  734.  
  735.     // don't run if paused
  736.     if (!sv_paused->value || maxclients->value > 1)
  737.     {
  738.         ge->RunFrame ();
  739.  
  740.         // never get more than one tic behind
  741.         if (sv.time < svs.realtime)
  742.         {
  743.             if (sv_showclamp->value)
  744.                 Com_Printf ("sv highclamp\n");
  745.             svs.realtime = sv.time;
  746.         }
  747.     }
  748.  
  749.     if (host_speeds->value)
  750.         time_after_game = Sys_Milliseconds ();
  751.  
  752. }
  753.  
  754. /*
  755. ==================
  756. SV_Frame
  757.  
  758. ==================
  759. */
  760. void SV_Frame (int msec)
  761. {
  762.     time_before_game = time_after_game = 0;
  763.  
  764.     // if server is not active, do nothing
  765.     if (!svs.initialized)
  766.         return;
  767.  
  768.     svs.realtime += msec;
  769.  
  770.     // keep the random time dependent
  771.     rand ();
  772.  
  773.     // check timeouts
  774.     SV_CheckTimeouts ();
  775.  
  776.     // get packets from clients
  777.     SV_ReadPackets ();
  778.  
  779.     // move autonomous things around if enough time has passed
  780.     if (!sv_timedemo->value && svs.realtime < sv.time)
  781.     {
  782.         // never let the time get too far off
  783.         if (sv.time - svs.realtime > 100)
  784.         {
  785.             if (sv_showclamp->value)
  786.                 Com_Printf ("sv lowclamp\n");
  787.             svs.realtime = sv.time - 100;
  788.         }
  789.         NET_Sleep(sv.time - svs.realtime);
  790.         return;
  791.     }
  792.  
  793.     // update ping based on the last known frame from all clients
  794.     SV_CalcPings ();
  795.  
  796.     // give the clients some timeslices
  797.     SV_GiveMsec ();
  798.  
  799.     // let everything in the world think and move
  800.     SV_RunGameFrame ();
  801.  
  802.     // send messages back to the clients that had packets read this frame
  803.     SV_SendClientMessages ();
  804.  
  805.     // save the entire world state if recording a serverdemo
  806.     SV_RecordDemoMessage ();
  807.  
  808.     // send a heartbeat to the master if needed
  809.     Master_Heartbeat ();
  810.  
  811.     // clear teleport flags, etc for next frame
  812.     SV_PrepWorldFrame ();
  813.  
  814. }
  815.  
  816. //============================================================================
  817.  
  818. /*
  819. ================
  820. Master_Heartbeat
  821.  
  822. Send a message to the master every few minutes to
  823. let it know we are alive, and log information
  824. ================
  825. */
  826. #define    HEARTBEAT_SECONDS    300
  827. void Master_Heartbeat (void)
  828. {
  829.     char        *string;
  830.     int            i;
  831.  
  832.     // pgm post3.19 change, cvar pointer not validated before dereferencing
  833.     if (!dedicated || !dedicated->value)
  834.         return;        // only dedicated servers send heartbeats
  835.  
  836.     // pgm post3.19 change, cvar pointer not validated before dereferencing
  837.     if (!public_server || !public_server->value)
  838.         return;        // a private dedicated game
  839.  
  840.     // check for time wraparound
  841.     if (svs.last_heartbeat > svs.realtime)
  842.         svs.last_heartbeat = svs.realtime;
  843.  
  844.     if (svs.realtime - svs.last_heartbeat < HEARTBEAT_SECONDS*1000)
  845.         return;        // not time to send yet
  846.  
  847.     svs.last_heartbeat = svs.realtime;
  848.  
  849.     // send the same string that we would give for a status OOB command
  850.     string = SV_StatusString();
  851.  
  852.     // send to group master
  853.     for (i=0 ; i<MAX_MASTERS ; i++)
  854.         if (master_adr[i].port)
  855.         {
  856.             Com_Printf ("Sending heartbeat to %s\n", NET_AdrToString (master_adr[i]));
  857.             Netchan_OutOfBandPrint (NS_SERVER, master_adr[i], "heartbeat\n%s", string);
  858.         }
  859. }
  860.  
  861. /*
  862. =================
  863. Master_Shutdown
  864.  
  865. Informs all masters that this server is going down
  866. =================
  867. */
  868. void Master_Shutdown (void)
  869. {
  870.     int            i;
  871.  
  872.     // pgm post3.19 change, cvar pointer not validated before dereferencing
  873.     if (!dedicated || !dedicated->value)
  874.         return;        // only dedicated servers send heartbeats
  875.  
  876.     // pgm post3.19 change, cvar pointer not validated before dereferencing
  877.     if (!public_server || !public_server->value)
  878.         return;        // a private dedicated game
  879.  
  880.     // send to group master
  881.     for (i=0 ; i<MAX_MASTERS ; i++)
  882.         if (master_adr[i].port)
  883.         {
  884.             if (i > 0)
  885.                 Com_Printf ("Sending heartbeat to %s\n", NET_AdrToString (master_adr[i]));
  886.             Netchan_OutOfBandPrint (NS_SERVER, master_adr[i], "shutdown");
  887.         }
  888. }
  889.  
  890. //============================================================================
  891.  
  892.  
  893. /*
  894. =================
  895. SV_UserinfoChanged
  896.  
  897. Pull specific info from a newly changed userinfo string
  898. into a more C freindly form.
  899. =================
  900. */
  901. void SV_UserinfoChanged (client_t *cl)
  902. {
  903.     char    *val;
  904.     int        i;
  905.  
  906.     // call prog code to allow overrides
  907.     ge->ClientUserinfoChanged (cl->edict, cl->userinfo);
  908.     
  909.     // name for C code
  910.     strncpy (cl->name, Info_ValueForKey (cl->userinfo, "name"), sizeof(cl->name)-1);
  911.     // mask off high bit
  912.     for (i=0 ; i<sizeof(cl->name) ; i++)
  913.         cl->name[i] &= 127;
  914.  
  915.     // rate command
  916.     val = Info_ValueForKey (cl->userinfo, "rate");
  917.     if (strlen(val))
  918.     {
  919.         i = atoi(val);
  920.         cl->rate = i;
  921.         if (cl->rate < 100)
  922.             cl->rate = 100;
  923.         if (cl->rate > 15000)
  924.             cl->rate = 15000;
  925.     }
  926.     else
  927.         cl->rate = 5000;
  928.  
  929.     // msg command
  930.     val = Info_ValueForKey (cl->userinfo, "msg");
  931.     if (strlen(val))
  932.     {
  933.         cl->messagelevel = atoi(val);
  934.     }
  935.  
  936. }
  937.  
  938.  
  939. //============================================================================
  940.  
  941. /*
  942. ===============
  943. SV_Init
  944.  
  945. Only called at quake2.exe startup, not for each game
  946. ===============
  947. */
  948. void SV_Init (void)
  949. {
  950.     SV_InitOperatorCommands    ();
  951.  
  952.     rcon_password = Cvar_Get ("rcon_password", "", 0);
  953.     Cvar_Get ("skill", "1", 0);
  954.     Cvar_Get ("deathmatch", "0", CVAR_LATCH);
  955.     Cvar_Get ("coop", "0", CVAR_LATCH);
  956.     Cvar_Get ("dmflags", va("%i", DF_INSTANT_ITEMS), CVAR_SERVERINFO);
  957.     Cvar_Get ("fraglimit", "0", CVAR_SERVERINFO);
  958.     Cvar_Get ("timelimit", "0", CVAR_SERVERINFO);
  959.     Cvar_Get ("cheats", "0", CVAR_SERVERINFO|CVAR_LATCH);
  960.     Cvar_Get ("protocol", va("%i", PROTOCOL_VERSION), CVAR_SERVERINFO|CVAR_NOSET);;
  961.     maxclients = Cvar_Get ("maxclients", "1", CVAR_SERVERINFO | CVAR_LATCH);
  962.     hostname = Cvar_Get ("hostname", "noname", CVAR_SERVERINFO | CVAR_ARCHIVE);
  963.     timeout = Cvar_Get ("timeout", "125", 0);
  964.     zombietime = Cvar_Get ("zombietime", "2", 0);
  965.     sv_showclamp = Cvar_Get ("showclamp", "0", 0);
  966.     sv_paused = Cvar_Get ("paused", "0", 0);
  967.     sv_timedemo = Cvar_Get ("timedemo", "0", 0);
  968.     sv_enforcetime = Cvar_Get ("sv_enforcetime", "0", 0);
  969.     allow_download = Cvar_Get ("allow_download", "1", CVAR_ARCHIVE);
  970.     allow_download_players  = Cvar_Get ("allow_download_players", "0", CVAR_ARCHIVE);
  971.     allow_download_models = Cvar_Get ("allow_download_models", "1", CVAR_ARCHIVE);
  972.     allow_download_sounds = Cvar_Get ("allow_download_sounds", "1", CVAR_ARCHIVE);
  973.     allow_download_maps      = Cvar_Get ("allow_download_maps", "1", CVAR_ARCHIVE);
  974.  
  975.     sv_noreload = Cvar_Get ("sv_noreload", "0", 0);
  976.  
  977.     sv_airaccelerate = Cvar_Get("sv_airaccelerate", "0", CVAR_LATCH);
  978.  
  979.     public_server = Cvar_Get ("public", "0", 0);
  980.  
  981.     sv_reconnect_limit = Cvar_Get ("sv_reconnect_limit", "3", CVAR_ARCHIVE);
  982.  
  983.     SZ_Init (&net_message, net_message_buffer, sizeof(net_message_buffer));
  984. }
  985.  
  986. /*
  987. ==================
  988. SV_FinalMessage
  989.  
  990. Used by SV_Shutdown to send a final message to all
  991. connected clients before the server goes down.  The messages are sent immediately,
  992. not just stuck on the outgoing message list, because the server is going
  993. to totally exit after returning from this function.
  994. ==================
  995. */
  996. void SV_FinalMessage (char *message, qboolean reconnect)
  997. {
  998.     int            i;
  999.     client_t    *cl;
  1000.     
  1001.     SZ_Clear (&net_message);
  1002.     MSG_WriteByte (&net_message, svc_print);
  1003.     MSG_WriteByte (&net_message, PRINT_HIGH);
  1004.     MSG_WriteString (&net_message, message);
  1005.  
  1006.     if (reconnect)
  1007.         MSG_WriteByte (&net_message, svc_reconnect);
  1008.     else
  1009.         MSG_WriteByte (&net_message, svc_disconnect);
  1010.  
  1011.     // send it twice
  1012.     // stagger the packets to crutch operating system limited buffers
  1013.  
  1014.     for (i=0, cl = svs.clients ; i<maxclients->value ; i++, cl++)
  1015.         if (cl->state >= cs_connected)
  1016.             Netchan_Transmit (&cl->netchan, net_message.cursize
  1017.             , net_message.data);
  1018.  
  1019.     for (i=0, cl = svs.clients ; i<maxclients->value ; i++, cl++)
  1020.         if (cl->state >= cs_connected)
  1021.             Netchan_Transmit (&cl->netchan, net_message.cursize
  1022.             , net_message.data);
  1023. }
  1024.  
  1025.  
  1026.  
  1027. /*
  1028. ================
  1029. SV_Shutdown
  1030.  
  1031. Called when each game quits,
  1032. before Sys_Quit or Sys_Error
  1033. ================
  1034. */
  1035. void SV_Shutdown (char *finalmsg, qboolean reconnect)
  1036. {
  1037.     extern void Con_Clear_f (void);
  1038.  
  1039.     if (svs.clients)
  1040.         SV_FinalMessage (finalmsg, reconnect);
  1041.  
  1042.     Master_Shutdown ();
  1043.     SV_ShutdownGameProgs ();
  1044.     Con_Clear_f(); //get rid of this if you want the messages back!
  1045.     // free current level
  1046.     if (sv.demofile)
  1047.         fclose (sv.demofile);
  1048.     memset (&sv, 0, sizeof(sv));
  1049.     Com_SetServerState (sv.state);
  1050.  
  1051.     // free server static data
  1052.     if (svs.clients)
  1053.         Z_Free (svs.clients);
  1054.     if (svs.client_entities)
  1055.         Z_Free (svs.client_entities);
  1056.     if (svs.demofile)
  1057.         fclose (svs.demofile);
  1058.     memset (&svs, 0, sizeof(svs));
  1059. }
  1060.  
  1061.